#pragma once

#include "newcpu.h"

static __forceinline uint get_word_prefetch(int o)
{
    uint v = regs.irc;
    regs.irc = get_wordi(m68k_getpc() + o);
    return v;
}
static __forceinline uint get_long_prefetch(int o)
{
    uint v = get_word_prefetch(o) << 16;
    v |= get_word_prefetch(o + 2);
    return v;
}

#ifdef CPUEMU_20

static __forceinline void do_cycles_ce020(int clocks)
{
    x_do_cycles(clocks * cpucycleunit);
}
static __forceinline void do_cycles_ce020_mem(int clocks, uint val)
{
    regs.ce020memcycles -= clocks * cpucycleunit;
    x_do_cycles_post(clocks * cpucycleunit, val);
}

static __forceinline void checkcycles_ce020()
{
    if (regs.ce020memcycles > 0)
        x_do_cycles_pre(regs.ce020memcycles);
    regs.ce020memcycles = 0;
}

static __forceinline uint mem_access_delay_long_read_ce020(uae_ptr addr)
{
    uint v;
    checkcycles_ce020();
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            if ((addr & 3) != 0)
            {
                v = wait_cpu_cycle_read_ce020(addr + 0, 1) << 16;
                v |= wait_cpu_cycle_read_ce020(addr + 2, 1) << 0;
                return v;
            }
            else
            {
                return wait_cpu_cycle_read_ce020(addr, -1);
            }
        case CE_MEMBANK_FAST:
            v = get_long(addr);
            if ((addr & 3) != 0)
                do_cycles_ce020_mem(2 * CPU020_MEM_CYCLE, v);
            else
                do_cycles_ce020_mem(1 * CPU020_MEM_CYCLE, v);
            return v;
        case CE_MEMBANK_FAST16BIT:
            v = get_long(addr);
            do_cycles_ce020_mem(2 * CPU020_MEM_CYCLE, v);
            return v;
    }
    return get_long(addr);
}

static __forceinline uint mem_access_delay_longi_read_ce020(uae_ptr addr)
{
    uint v;
    checkcycles_ce020();
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            if ((addr & 3) != 0)
            {
                v = wait_cpu_cycle_read_ce020(addr + 0, 1) << 16;
                v |= wait_cpu_cycle_read_ce020(addr + 2, 1) << 0;
                return v;
            }
            else
            {
                return wait_cpu_cycle_read_ce020(addr, -1);
            }
        case CE_MEMBANK_FAST:
            v = get_longi(addr);
            if ((addr & 3) != 0)
                do_cycles_ce020_mem(2 * CPU020_MEM_CYCLE, v);
            else
                do_cycles_ce020_mem(1 * CPU020_MEM_CYCLE, v);
            return v;
        case CE_MEMBANK_FAST16BIT:
            v = get_longi(addr);
            do_cycles_ce020_mem(2 * CPU020_MEM_CYCLE, v);
            return v;
    }
    return get_longi(addr);
}

static __forceinline uint mem_access_delay_word_read_ce020(uae_ptr addr)
{
    uint v;
    checkcycles_ce020();
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            if ((addr & 3) == 3)
            {
                v = wait_cpu_cycle_read_ce020(addr + 0, 0) << 8;
                v |= wait_cpu_cycle_read_ce020(addr + 1, 0) << 0;
                return v;
            }
            else
            {
                return wait_cpu_cycle_read_ce020(addr, 1);
            }
        case CE_MEMBANK_FAST:
        case CE_MEMBANK_FAST16BIT:
            v = get_word(addr);
            if ((addr & 3) == 3)
                do_cycles_ce020_mem(2 * CPU020_MEM_CYCLE, v);
            else
                do_cycles_ce020_mem(1 * CPU020_MEM_CYCLE, v);
            return v;
    }
    return get_word(addr);
}

static __forceinline uint mem_access_delay_wordi_read_ce020(uae_ptr addr)
{
    uint v;
    checkcycles_ce020();
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            return wait_cpu_cycle_read_ce020(addr, 1);
        case CE_MEMBANK_FAST:
        case CE_MEMBANK_FAST16BIT:
            v = get_wordi(addr);
            do_cycles_ce020_mem(1 * CPU020_MEM_CYCLE, v);
            return v;
    }
    return get_wordi(addr);
}

static __forceinline uint mem_access_delay_byte_read_ce020(uae_ptr addr)
{
    uint v;
    checkcycles_ce020();
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            return wait_cpu_cycle_read_ce020(addr, 0);
        case CE_MEMBANK_FAST:
        case CE_MEMBANK_FAST16BIT:
            v = get_byte(addr);
            do_cycles_ce020_mem(1 * CPU020_MEM_CYCLE, v);
            return v;
    }
    return get_byte(addr);
}

static __forceinline void mem_access_delay_byte_write_ce020(uae_ptr addr, uint v)
{
    checkcycles_ce020();
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            wait_cpu_cycle_write_ce020(addr, 0, v);
            return;
        case CE_MEMBANK_FAST:
        case CE_MEMBANK_FAST16BIT:
            put_byte(addr, v);
            do_cycles_ce020_mem(1 * CPU020_MEM_CYCLE, v);
            return;
    }
    put_byte(addr, v);
}

static __forceinline void mem_access_delay_word_write_ce020(uae_ptr addr, uint v)
{
    checkcycles_ce020();
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            if ((addr & 3) == 3)
            {
                wait_cpu_cycle_write_ce020(addr + 0, 0, (v >> 8) & 0xff);
                wait_cpu_cycle_write_ce020(addr + 1, 0, (v >> 0) & 0xff);
            }
            else
            {
                wait_cpu_cycle_write_ce020(addr + 0, 1, v);
            }
            return;
            break;
        case CE_MEMBANK_FAST:
        case CE_MEMBANK_FAST16BIT:
            put_word(addr, v);
            if ((addr & 3) == 3)
                do_cycles_ce020_mem(2 * CPU020_MEM_CYCLE, v);
            else
                do_cycles_ce020_mem(1 * CPU020_MEM_CYCLE, v);
            return;
    }
    put_word(addr, v);
}

static __forceinline void mem_access_delay_long_write_ce020(uae_ptr addr, uint v)
{
    checkcycles_ce020();
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            if ((addr & 3) == 3)
            {
                wait_cpu_cycle_write_ce020(addr + 0, 1, (v >> 16) & 0xffff);
                wait_cpu_cycle_write_ce020(addr + 2, 1, (v >> 0) & 0xffff);
            }
            else
            {
                wait_cpu_cycle_write_ce020(addr + 0, -1, v);
            }
            return;
            break;
        case CE_MEMBANK_FAST:
            put_long(addr, v);
            if ((addr & 3) != 0)
                do_cycles_ce020_mem(2 * CPU020_MEM_CYCLE, v);
            else
                do_cycles_ce020_mem(1 * CPU020_MEM_CYCLE, v);
            return;
        case CE_MEMBANK_FAST16BIT:
            put_long(addr, v);
            do_cycles_ce020_mem(2 * CPU020_MEM_CYCLE, v);
            return;
    }
    put_long(addr, v);
}

static __forceinline uint get_long_ce020(uae_ptr addr)
{
    return mem_access_delay_long_read_ce020(addr);
}
static __forceinline uint get_word_ce020(uae_ptr addr)
{
    return mem_access_delay_word_read_ce020(addr);
}
static __forceinline uint get_byte_ce020(uae_ptr addr)
{
    return mem_access_delay_byte_read_ce020(addr);
}

static __forceinline void put_long_ce020(uae_ptr addr, uint v)
{
    mem_access_delay_long_write_ce020(addr, v);
}
static __forceinline void put_word_ce020(uae_ptr addr, uint v)
{
    mem_access_delay_word_write_ce020(addr, v);
}
static __forceinline void put_byte_ce020(uae_ptr addr, uint v)
{
    mem_access_delay_byte_write_ce020(addr, v);
}

extern uint get_word_ce020_prefetch(int);

static __forceinline uint get_long_ce020_prefetch(int o)
{
    uint v;
    v = get_word_ce020_prefetch(o) << 16;
    v |= get_word_ce020_prefetch(o + 2);
    return v;
}

static __forceinline uint next_iword_020ce()
{
    uint r = get_word_ce020_prefetch(0);
    m68k_incpc(2);
    return r;
}
static __forceinline uint next_ilong_020ce()
{
    uint r = get_long_ce020_prefetch(0);
    m68k_incpc(4);
    return r;
}

static __forceinline void m68k_do_bsr_ce020(uae_ptr oldpc, int offset)
{
    m68k_areg(regs, 7) -= 4;
    x_put_long(m68k_areg(regs, 7), oldpc);
    m68k_incpc(offset);
}
static __forceinline void m68k_do_rts_ce020()
{
    m68k_setpc(x_get_long(m68k_areg(regs, 7)));
    m68k_areg(regs, 7) += 4;
}

#endif

#ifdef CPUEMU_21

extern uint get_word_ce030_prefetch(int);
extern void write_dcache030(uae_ptr, uint, int);
extern uint read_dcache030(uae_ptr, int);

static __forceinline void put_long_ce030(uae_ptr addr, uint v)
{
    write_dcache030(addr, v, 2);
    mem_access_delay_long_write_ce020(addr, v);
}
static __forceinline void put_word_ce030(uae_ptr addr, uint v)
{
    write_dcache030(addr, v, 1);
    mem_access_delay_word_write_ce020(addr, v);
}
static __forceinline void put_byte_ce030(uae_ptr addr, uint v)
{
    write_dcache030(addr, v, 0);
    mem_access_delay_byte_write_ce020(addr, v);
}
static __forceinline uint get_long_ce030(uae_ptr addr)
{
    return read_dcache030(addr, 2);
}
static __forceinline uint get_word_ce030(uae_ptr addr)
{
    return read_dcache030(addr, 1);
}
static __forceinline uint get_byte_ce030(uae_ptr addr)
{
    return read_dcache030(addr, 0);
}

static __forceinline uint get_long_ce030_prefetch(int o)
{
    uint v;
    v = get_word_ce030_prefetch(o) << 16;
    v |= get_word_ce030_prefetch(o + 2);
    return v;
}

static __forceinline uint next_iword_030ce()
{
    uint r = get_word_ce030_prefetch(0);
    m68k_incpc(2);
    return r;
}
static __forceinline uint next_ilong_030ce()
{
    uint r = get_long_ce030_prefetch(0);
    m68k_incpc(4);
    return r;
}

static __forceinline void m68k_do_bsr_ce030(uae_ptr oldpc, int offset)
{
    m68k_areg(regs, 7) -= 4;
    put_long_ce030(m68k_areg(regs, 7), oldpc);
    m68k_incpc(offset);
}
static __forceinline void m68k_do_rts_ce030()
{
    m68k_setpc(get_long_ce030(m68k_areg(regs, 7)));
    m68k_areg(regs, 7) += 4;
}

#endif

#ifdef CPUEMU_12

static __forceinline void do_cycles_ce000(int clocks)
{
    x_do_cycles(clocks * cpucycleunit);
}

static __forceinline void ipl_fetch()
{
    regs.ipl = regs.ipl_pin;
}

static __forceinline uint mem_access_delay_word_read(uae_ptr addr)
{
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            return wait_cpu_cycle_read(addr, 1);
        case CE_MEMBANK_FAST:
        case CE_MEMBANK_FAST16BIT:
            uint v = get_word(addr);
            x_do_cycles_post(4 * cpucycleunit, v);
            return v;
    }
    return get_word(addr);
}
static __forceinline uint mem_access_delay_wordi_read(uae_ptr addr)
{
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            return wait_cpu_cycle_read(addr, 1);
        case CE_MEMBANK_FAST:
        case CE_MEMBANK_FAST16BIT:
            uint v = get_wordi(addr);
            x_do_cycles_post(4 * cpucycleunit, v);
            return v;
    }
    return get_wordi(addr);
}

static __forceinline uint mem_access_delay_byte_read(uae_ptr addr)
{
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            return wait_cpu_cycle_read(addr, 0);
        case CE_MEMBANK_FAST:
        case CE_MEMBANK_FAST16BIT:
            uint v = get_byte(addr);
            x_do_cycles_post(4 * cpucycleunit, v);
            return v;
    }
    return get_byte(addr);
}
static __forceinline void mem_access_delay_byte_write(uae_ptr addr, uint v)
{
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            wait_cpu_cycle_write(addr, 0, v);
            return;
        case CE_MEMBANK_FAST:
        case CE_MEMBANK_FAST16BIT:
            put_byte(addr, v);
            x_do_cycles_post(4 * cpucycleunit, v);
            return;
    }
    put_byte(addr, v);
}
static __forceinline void mem_access_delay_word_write(uae_ptr addr, uint v)
{
    switch (ce_banktype[addr >> 16])
    {
        case CE_MEMBANK_CHIP:
            wait_cpu_cycle_write(addr, 1, v);
            return;
        case CE_MEMBANK_FAST:
        case CE_MEMBANK_FAST16BIT:
            put_word(addr, v);
            x_do_cycles_post(4 * cpucycleunit, v);
            return;
    }
    put_word(addr, v);
}

static __forceinline uint get_long_ce000(uae_ptr addr)
{
    uint v = mem_access_delay_word_read(addr) << 16;
    v |= mem_access_delay_word_read(addr + 2);
    return v;
}
static __forceinline uint get_word_ce000(uae_ptr addr)
{
    return mem_access_delay_word_read(addr);
}
static __forceinline uint get_wordi_ce000(int offset)
{
    return mem_access_delay_wordi_read(m68k_getpc() + offset);
}
static __forceinline uint get_byte_ce000(uae_ptr addr)
{
    return mem_access_delay_byte_read(addr);
}
static __forceinline uint get_word_ce000_prefetch(int o)
{
    uint v = regs.irc;
    regs.irc = x_get_iword(o);
    return v;
}

static __forceinline void put_long_ce000(uae_ptr addr, uint v)
{
    mem_access_delay_word_write(addr, v >> 16);
    mem_access_delay_word_write(addr + 2, v);
}
static __forceinline void put_word_ce000(uae_ptr addr, uint v)
{
    mem_access_delay_word_write(addr, v);
}
static __forceinline void put_byte_ce000(uae_ptr addr, uint v)
{
    mem_access_delay_byte_write(addr, v);
}

static __forceinline void m68k_do_rts_ce()
{
    uae_ptr pc;
    pc = x_get_word(m68k_areg(regs, 7)) << 16;
    pc |= x_get_word(m68k_areg(regs, 7) + 2);
    m68k_areg(regs, 7) += 4;
    if (pc & 1)
        exception3(0x4e75, pc);
    else
        m68k_setpc(pc);
}

static __forceinline void m68k_do_bsr_ce(uae_ptr oldpc, int offset)
{
    m68k_areg(regs, 7) -= 4;
    x_put_word(m68k_areg(regs, 7), oldpc >> 16);
    x_put_word(m68k_areg(regs, 7) + 2, oldpc);
    m68k_incpc(offset);
}

static __forceinline void m68k_do_jsr_ce(uae_ptr oldpc, uae_ptr dest)
{
    m68k_areg(regs, 7) -= 4;
    x_put_word(m68k_areg(regs, 7), oldpc >> 16);
    x_put_word(m68k_areg(regs, 7) + 2, oldpc);
    m68k_setpc(dest);
}

#endif

static __forceinline uint get_disp_ea_000(uint base, uint dp)
{
    int reg = (dp >> 12) & 15;
    int regd = regs.regs[reg];
    if ((dp & 0x800) == 0)
        regd = (int)(short)regd;
    return base + (sbyte)dp + regd;
}